#include "mshell.h"
#include "device.h"
#include "serial.h"

static SERIAL_t _serial = NULL;
static shell_table_t _shell_table = NULL;
static uint16_t _shell_table_sz = 0;
static char password[16];

static void _delay(uint32_t ms)
{
    system_delay_ms(ms);
}

static uint8_t _get_byte(uint8_t *b)
{
    uint8_t ret = 0;
    if(_serial->readable() > 0)
    {
        _serial->read(0, b, 1, NULL);
        ret = 1;
    }
    return ret;
}

static void _send_byte(uint8_t b)
{
    _serial->write(0, &b, 1, NULL);
}

static void _send_string(char *string)
{
    if(string == NULL)
    {
        return;
    }
    uint16_t len = strlen(string);
    if(len > 0)
    {
        _serial->write(0, (uint8_t *)string, len, NULL);
    }
}

static void _flushinput(void)
{
    _serial->ioctl(CMD_REV_INIT, NULL);
}

static uint8_t _str_compare(const char *str1, const char *str2)
{
    while((*str1 != '\0')&&(*str2 != '\0'))
    {
        if(*str1++ != *str2++)
            return 1;
    }
    if(*str1 == '\0')
    {
        if((*str2 == '\0')||(*str2 == ' '))
            return 0;
        else
            return 1;
    }
    if(*str2 == '\0')
    {
        if((*str1 == '\0')||(*str1 == ' '))
            return 0;
        else
            return 1;
    }
    return 1;
}

static void _help(char *str)
{
    _send_string("\r\nsupport command\r\n\r\n");
    for(int i = 0; i < _shell_table_sz; i++)
    {
        if(strstr((char *)(_shell_table[i].name),"null"))
        {
            break;
        }
        _send_string((char *)(_shell_table[i].name));
        _send_string("\r\n");
    }
}

static char *_search_name(char *string)
{
    uint8_t key = 0xff,key_count = 0;

    if(_shell_table == NULL)
        return NULL;
    for(int i = 0; i < _shell_table_sz; i++)
    {
        if(strstr((char *)(_shell_table[i].name), string))
        {
            key_count++;
            key = i;
        }
    }
    if(key_count == 1)
    {
        return (char *)(_shell_table[key].name);
    }
    return NULL;
}

static uint8_t _search_function(char *string)
{
    if(_shell_table == NULL)
        return 0xfe;

    if(strcmp(string,"help") == 0)
    {
        _help(string);
        return 0xfc;
    }

    for(int i = 0; i < _shell_table_sz; i++)
    {
        if(_shell_table[i].func == NULL)
        {
            break;
        }
        if(_str_compare(string, _shell_table[i].name) == 0)
        {
            if((strstr(string, "-h"))||(strstr(string, "--help")))
            {
                if((_shell_table[i].help != NULL)&&(strlen((char *)(_shell_table[i].help)) > 0))
                {
                    _send_string((char *)(_shell_table[i].help));
                }
            }
            else
            {
                _shell_table[i].func(string);
            }
            return i;
        }
    }
    return 0xff;
}

int mshell_set_table(shell_table_t table, uint32_t table_sz)
{
    if((table == NULL)||(table_sz == 0))
    {
        return -1;
    }
    _shell_table = table;
    _shell_table_sz = table_sz;

    return 1;
}

void mshell_set_password(char *pwd)
{
    if(pwd == NULL)
        return;
    strncpy(password,pwd,16);
}

int mshell_password_verify(uint32_t delay)
{
    int ret = 0;
    uint8_t revbuf[16] = {0};
    uint16_t rcount = 0;
    uint8_t rbyte = 0;
    uint8_t errcount = 0;

    _send_string("pwd>");
    _flushinput();

    uint32_t current_tick = system_get_tick();
    while(system_get_tick() - current_tick <= delay)
    {
        if(_get_byte(&rbyte) > 0)
        {
            if(rbyte == '\r')
            {
                revbuf[rcount] = 0;
                if(strcmp(password, (char *)revbuf) == 0)
                {
                    ret = 1;
                    break;
                }
                if(errcount++ > 3)
                {
                    _send_string("error\r\n");
                    break;
                }
                rcount = 0;
                _send_string("\r\npwd>");
            }
            else if(rbyte == CTRLC)
            {
                rcount = 0;
                _send_string("\r\npwd>");
            }
            else if(rbyte == BACK)
            {
                if(rcount > 0)
                {
                    _send_string("\b \b");
                    rcount--;
                }
            }
            else
            {
                if(rcount < 15)
                {
                    revbuf[rcount++] = rbyte;
                }
                _send_byte('*');
            }
        }
        else
        {
            _delay(50);
        }
    }
    return ret;
}

void mshell_handle(void)
{
    uint8_t revbuf[64] = {0};
    uint16_t rcount = 0;
    uint8_t rbyte = 0;

    _send_string("\r\nmdsh>");
    _flushinput();
    while(1)
    {
        if(_get_byte(&rbyte) > 0)
        {
            if(rbyte == '\r')
            {
                revbuf[rcount] = 0;
                _search_function((char *)revbuf);
                _flushinput();
                rcount = 0;
                _send_string("\r\nmdsh>");
            }
            else if(rbyte == CTRLC)
            {
                rcount = 0;
                _send_string("\r\nmdsh>");
            }
            else if(rbyte == TABLE)
            {
                revbuf[rcount] = 0;
                char *_key = _search_name((char *)revbuf);
                if(_key != NULL)
                {
                    strcpy((char *)revbuf,_key);
                    _send_string((char *)&revbuf[rcount]);
                    rcount = strlen(_key);
                }
            }
            else if(rbyte == BACK)
            {
                if(rcount > 0)
                {
                    _send_string("\b \b");
                    rcount--;
                }
            }
            else if((rbyte == 0x1b)||(rbyte == 0x5b)||((rbyte >= 0x41)&(rbyte <= 0x44)))
            {
                // if(rbyte == 0x41)
                // {
                //     // _send_string("up");
                // }
                // else if(rbyte == 0x42)
                // {
                //     // _send_string("down");
                // }
                // else if(rbyte == 0x43)
                // {
                //     // _send_string("rigth");
                // }
                // else if(rbyte == 0x44)
                // {
                //     // _send_string("left");
                // }
            }
            else
            {
                if(rcount < 63)
                {
                    revbuf[rcount++] = rbyte;
                }
                _send_byte(rbyte);
            }
        }
        else
        {
            _delay(50);
        }
    }
}

void mshell_init(void *dev)
{
    _serial = dev;
    memset(password,0x00,16);
}

void *mshell_dev(void)
{
    return _serial;
}

int mshell_printf(const char *cmd_expr, ...)
{
    char shellbuf[128];
    va_list args;

    va_start(args, cmd_expr);
    int shell_len = vsnprintf(shellbuf, sizeof(shellbuf), cmd_expr, args);
    va_end(args);

    _serial->write(0, shellbuf, shell_len, NULL);

    return shell_len;
}
